/**
 * 
 */
package com.effectivsocial.rest.interceptor;

import java.util.List;
import java.util.TreeMap;

import javax.inject.Inject;
import javax.inject.Named;

import jodd.cache.LRUCache;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import com.effectivsocial.domain.SocialNetwork;
import com.effectivsocial.domain.User;
import com.effectivsocial.service.api.SocialNetworkService;

/**
 * @author Dhrubo
 * 
 */
@Slf4j
@Named
public class ApiKeyValidationInterceptor extends
		AbstractPhaseInterceptor<Message> {

	@Inject
	private SocialNetworkService socialNetworkService;

	private static final LRUCache<String, SocialNetwork> networkCache = new LRUCache<String, SocialNetwork>(
			50);

	public ApiKeyValidationInterceptor() {
		super(Phase.PRE_INVOKE);
	}
	
	private String getApiKeyFromHeader(Message message) {
		String apiKey = null;
		TreeMap header = (TreeMap) message.get(Message.PROTOCOL_HEADERS);
		List apiKeyNode = (List) header.get("apiKey");
		
		if(log.isDebugEnabled()) {
			log.debug("Header = {}", header);
			log.debug("Header apiKeyNode = {}", apiKeyNode);
		}
		

		if (apiKeyNode != null && apiKeyNode.size() > 0) {
			apiKey = (String) apiKeyNode.get(0);
		}

		if (StringUtil.isEmpty(apiKey) || StringUtil.isBlank(apiKey)) {
			throw new RuntimeException("Api Key is missing in HTTP Header.");
		}
		
		return apiKey;
	}

	public void handleMessage(Message message) throws Fault {
		
		// load Social network for this api key
		String apiKey = getApiKeyFromHeader(message);
		
		
		if(log.isDebugEnabled()){
			log.debug("Retrieving network for the given api key from the cache.");
		}
		
		SocialNetwork network = networkCache.get(apiKey);
		
		if(network == null){
			//network not in cache, lookup in database
			if(log.isDebugEnabled()){
				log.debug("Network not in cache. Falling back to database for network.");
			}
			
			network = socialNetworkService.findBySharedSecret(apiKey);
			
		}
		
		if (network == null) {
			throw new RuntimeException(
					"Api key not linked to any network. Check the key and retry operation.");
		}
		
		//store in cache
		
		if(log.isDebugEnabled()){
			log.debug("Storing network details in cache.");
		}
		
		networkCache.put(apiKey, network);
		
		if(log.isDebugEnabled()) {
		
			log.debug("Social Network = {}", network.getName());
			log.debug("Social Type = {}", network.getNetworkType().getName());
		}
		
		validateNetworkOwnerAuthentication(message, network);
		
		
	}

	private void validateNetworkOwnerAuthentication(Message message,
			SocialNetwork network) {
		
		AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
		String userName = policy.getUserName();
		
		List<User> owners = network.getUsers();

		boolean authenticatedUserNetworkOwner = false;

		for (User u : owners) {
			if (StringUtil.equals(u.getEmail(), userName)) {
				authenticatedUserNetworkOwner = true;
				break;
			}
		}

		if (authenticatedUserNetworkOwner) {
			log.info("Authenticated user owns the network hence operation can can be performed.Adding network information");
			message.setContextualProperty("network", network);
		} else {
			throw new RuntimeException(
					"User , apikey combination not linked to the network.");
		}

	}

}
